package org.testile.placeholderscript;

import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

/**
 *
 * @author hvizmar
 */
public class PlaceholderReplaceScriptEngine extends AbstractScriptEngine {

	private List<PlaceholderTransformer> transformers;
	private static final String PLACEHOLDER_PREFIX = "${";
	private static final String PLACEHOLDER_SUFFIX = "}";

	public PlaceholderReplaceScriptEngine(List<PlaceholderTransformer> transformers) {
		this.transformers = transformers;
		for (PlaceholderTransformer placeholderTransformer : transformers) {
			placeholderTransformer.registerScriptEngine(this);
		}
	}

	public Object eval(Reader reader, ScriptContext context) throws ScriptException {
		Scanner scanner = new Scanner(reader);
		StringBuilder builder = new StringBuilder();

		while (scanner.hasNextLine()) {
			String line = scanner.nextLine();
			String result = (String) eval(line, context);
			builder.append(result);
		}

		return builder.toString();
	}

	public Object eval(String script, ScriptContext context) throws ScriptException {
		List<Replacement> replacements = getReplacements(script);
		return constructTransformedScript(script, replacements);
	}

	private List<Replacement> getReplacements(String originalScript) {
		StringBuffer buf = new StringBuffer(originalScript);
		List<Replacement> replacements = new ArrayList<Replacement>();

		int startIndex = originalScript.indexOf(PLACEHOLDER_PREFIX);
		while (startIndex != -1) {
			int endIndex = originalScript.indexOf(PLACEHOLDER_SUFFIX, startIndex);
			if (endIndex != -1) {
				String placeholder = buf.substring(startIndex, endIndex + 1);
				String transformation = getTransformation(placeholder);
				replacements.add(new Replacement(startIndex, endIndex + 1, transformation));
				startIndex = originalScript.indexOf(PLACEHOLDER_PREFIX, endIndex);
			} else {
				startIndex = -1;
			}
		}

		return replacements;
	}

	private String getTransformation(String original) {
		String trimmedOriginal = original.substring(2, original.length() - 1); // original without placeholder characters original = ${<trimmedOriginal>}
		for (PlaceholderTransformer transformer : transformers) {
			if(transformer.isSuitable(trimmedOriginal)) {
				return transformer.transform(trimmedOriginal);
			}
		}

		return original;
	}

	private String constructTransformedScript(String originalScript, List<Replacement> replacements) {
		StringBuilder builder = new StringBuilder();

		int lastEnd = 0;
		for (Replacement replacement : replacements) {
			builder.append(originalScript.substring(lastEnd, replacement.start));
			builder.append(replacement.replacement);
			lastEnd = replacement.end;
		}

		builder.append(originalScript.substring(lastEnd, originalScript.length()));

		return builder.toString();
	}

	public Bindings createBindings() {
		return new SimpleBindings();
	}

	public ScriptEngineFactory getFactory() {
		return new PlaceholderReplaceScriptEngineFactory();
	}

	private static final class Replacement {
		private final int start;
		private final int end;
		private final String replacement;

		private Replacement(int start, int end, String replacement) {
			this.start = start;
			this.end = end;
			this.replacement = replacement;
		}
	}
}
